package com.da.javatest.thread.juc.aqs;

import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.LockSupport;

import lombok.Getter;
import lombok.Setter;
import sun.misc.Unsafe;

/**
 * @author chenlida
 * @date 2020/9/6 15:52
 * @description 简单的AQS实现公平锁，AQS其实就是围绕，CAS、自旋、LockSupport展开的
 * AQS特性：阻塞等待队列、共享/独占、公平/非公平、可重入、允许中断
 */
public class MyLock {
    /**
     * 当前加锁状态，记录加锁次数
     */
    @Getter
    @Setter
    private volatile int state = 0;
    /**
     * 当前持有锁的线程
     */
    @Getter
    @Setter
    private Thread lockHolder;
    private ConcurrentLinkedQueue<Thread> waiters = new ConcurrentLinkedQueue<>();

    private static Unsafe unsafe;
    private static long stateOffset;

    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe)field.get(null);
            // 获取state在MyLock类中的内存偏移量
            stateOffset = unsafe.objectFieldOffset(MyLock.class.getDeclaredField("state"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 加锁
     */
    public void lock() {
        // 加锁成功
        if (acquire()) {
            return;
        }

        Thread current = Thread.currentThread();
        waiters.add(current);

        for (; ; ) {
            if (current == waiters.peek() && acquire()) {
                // 获取锁成功后需要从队列里面移除
                waiters.poll();
                return;
            }
            // 阻塞当前线程
            LockSupport.park(current);
        }
    }

    public boolean acquire() {
        Thread current = Thread.currentThread();
        // 初始状态
        int c = getState();
        if (c == 0) { // 同步器还没被持有
            if ((waiters.size() == 0 || current == waiters.peek()) && compareAndSwapState(0, 1)) {
                setLockHolder(current);
                return true;
            }
        }

        return false;
    }

    /**
     * 解锁
     */
    public void unlock() {
        if (Thread.currentThread() != lockHolder) {
            throw new RuntimeException("LockHolder is not current thread!");
        }
        int state = getState();
        if (compareAndSwapState(state, 0)) {
            setLockHolder(null);
            Thread first = waiters.peek();
            if (first != null) {
                LockSupport.unpark(first);
            }
        }
    }

    /**
     * 原子操作
     */
    private boolean compareAndSwapState(int except, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, except, update);
    }
}
